﻿using Mes.Infrastructure.Data.BoundedContext.Common;
using Mes.Infrastructure.Data.BoundedContext.Configurations;
using Mes.Infrastructure.Data.BoundedContext.Extensions;
using Mes.Domain.BoundedContext.Entities;
using Mes.Domain.BoundedContext.EntityBase;
using Microsoft.EntityFrameworkCore;
using Microsoft.Extensions.Options;

namespace Mes.Infrastructure.Data.BoundedContext
{
    public class MesDbContext : DbContext
    {
        public MesDbContext()
        {

        }
        protected IOptionsMonitor<DbConfiguration> DbOptions { get; }

        public MesDbContext(DbContextOptions<MesDbContext> options, IOptionsMonitor<DbConfiguration> dbOptions) : base(options)
        {
            DbOptions = dbOptions;
        }
        protected override void OnConfiguring(DbContextOptionsBuilder optionsBuilder)
        {
            base.OnConfiguring(optionsBuilder);
            optionsBuilder.UseNpgsql(DbOptions.CurrentValue.MainMasterConnectionString, c => c.MigrationsAssembly("Mes.Application.Web"));
        }

        protected override void OnModelCreating(ModelBuilder modelBuilder)
        {
            //Register all DbSets to DbContext
            var modelassembly = AppDomain.CurrentDomain.GetAssemblies().Where(x => !x.IsDynamic && x.FullName.Contains("Mes.Domain.BoundedContext")).FirstOrDefault();
            foreach (Type type in modelassembly.ExportedTypes)
            {
                if (type.IsClass && type != typeof(EntityBase) && typeof(EntityBase).IsAssignableFrom(type))
                {
                    var method = modelBuilder.GetType().GetMethods().Where(x => x.Name == "Entity").FirstOrDefault();

                    if (method != null)
                    {
                        method = method.MakeGenericMethod(new Type[] { type });
                        method.Invoke(modelBuilder, null);
                    }
                }
                if (type.IsClass && type != typeof(NoIdEntityBase) && typeof(NoIdEntityBase).IsAssignableFrom(type))
                {
                    var method = modelBuilder.GetType().GetMethods().Where(x => x.Name == "Entity").FirstOrDefault();

                    if (method != null)
                    {
                        method = method.MakeGenericMethod(new Type[] { type });
                        method.Invoke(modelBuilder, null);
                    }
                }
            }

            //Entity fields are mapped to the database and converted to lowercase
            foreach (var entity in modelBuilder.Model.GetEntityTypes())
            {
                // Replace table names 
                entity.SetTableName(entity.GetTableName().ToSnakeCase());

                // Replace column names    
                foreach (var property in entity.GetProperties())
                {
                    property.SetColumnName(property.GetColumnName().ToSnakeCase());
                }

                // Replace key names    
                foreach (var key in entity.GetKeys())
                {
                    key.SetName(key.GetName().ToSnakeCase());
                }
            }

            //Load all IEntityTypeConfiguration from the current assembly
            modelBuilder.ApplyConfigurationsFromAssembly(this.GetType().Assembly);
            modelBuilder.BuildSoftDeleteEntities();
            base.OnModelCreating(modelBuilder);
        }
    }
}
